package com.swsc.solarverse.service.common;

import com.swsc.solarverse.common.Constant;
import com.swsc.solarverse.common.util.CalculateUtils;
import com.swsc.solarverse.common.util.LocalDateTimeUtils;
import com.swsc.solarverse.common.util.RegexUtils;
import com.swsc.solarverse.dao.entity.analysis.StringEfficiencyRecordDO;
import com.swsc.solarverse.dao.entity.forward.*;
import com.swsc.solarverse.model.biz.StringDataPowerDTO;
import com.swsc.solarverse.model.biz.StringEfficiencyDTO;
import com.swsc.solarverse.model.biz.StringVoltageCurrentInvalidDataDTO;
import org.apache.commons.collections4.CollectionUtils;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author chencl
 * @Date 2024/6/4 14:54
 * @Version 1.0
 * @Description 转发设备数据
 */
@lombok.extern.slf4j.Slf4j
public class ForwardDeviceDataHelper {
    public static void calculateStringEfficiencyRecordBySubmatrix(Integer organNo, String submatrixCode, LocalDateTime dataTime, LocalDateTime dateTime,
                                                                  List<ForwardStringDataDO> submatrixForwardStringDataDos,
                                                                  List<StringEfficiencyRecordDO> efficiencyRecordResults) {
        if (CollectionUtils.isNotEmpty(submatrixForwardStringDataDos)) {
            // 零电流支路判定，取早上11:00-11:30分钟级电流数据，判定30组数据，如果存在20组数据以上相同或小于1A，则判定该支路为零电流支路，下面过程不参与计算
            Collection<String> excludeStringCodeSet = ForwardDeviceDataHelper.filterInvalidStringCurrent(submatrixForwardStringDataDos,
                    dataTime.withHour(11).withMinute(1).withSecond(0).withNano(0),
                    dataTime.withHour(11).withMinute(30).withSecond(0).withNano(0));
            StringVoltageCurrentInvalidDataDTO invalidDataDto = ForwardDeviceDataHelper.getInvalidStringVoltageCurrent(submatrixForwardStringDataDos,
                    dataTime.withHour(11).withMinute(1).withSecond(0).withNano(0),
                    dataTime.withHour(11).withMinute(30).withSecond(0).withNano(0));
            log.info("低效组串发电量计算-无效组串: {}", excludeStringCodeSet);
            if (CollectionUtils.isEmpty(submatrixForwardStringDataDos)) {
                log.info("低效组串发电量计算-过滤设备电压电流数据后无数据: {}, {}", organNo, submatrixCode);
                return;
            }
            // 计算组串能效/电量
            List<StringEfficiencyDTO> efficiencyDtos = ForwardDeviceDataHelper.calculateStringEfficiency(submatrixForwardStringDataDos);
            ForwardDeviceDataHelper.markStringEfficiencyRecordInvalidVoltageCurrent(invalidDataDto, efficiencyDtos);
            // 国能分区编码和子阵编码相同, 其他电站根据情况修改
            List<StringEfficiencyRecordDO> efficiencyRecordDos = ForwardDeviceDataHelper.transferStringEfficiencyRecord(efficiencyDtos, LocalDateTimeUtils.getDayStart(dataTime), submatrixCode, submatrixCode);
            // 计算子阵的平均发电量
            ForwardDeviceDataHelper.calculateSubmatrixAvg(efficiencyRecordDos, submatrixCode, dateTime, excludeStringCodeSet);
            if (CollectionUtils.isNotEmpty(efficiencyRecordDos)) {
                efficiencyRecordResults.addAll(efficiencyRecordDos);
            }
            log.info("低效组串发电量计算-子阵[{}, {}]计算完成...", organNo, submatrixCode);
        }
    }

    public static void calculateSubmatrixAvg(List<StringEfficiencyRecordDO> efficiencyRecordDos, String submatrixCode, LocalDateTime dateTime, Collection<String> excludeStringCodeSet) {
        if (CollectionUtils.isNotEmpty(efficiencyRecordDos)) {
            List<StringEfficiencyRecordDO> validEfficiencyRecordDos = efficiencyRecordDos;
            if (CollectionUtils.isNotEmpty(excludeStringCodeSet)) {
                {
                    validEfficiencyRecordDos = efficiencyRecordDos.stream().filter(rd -> !excludeStringCodeSet.contains(rd.getDeviceCode())).collect(Collectors.toList());
                }
            }
            Integer submatrixStringValidNum = validEfficiencyRecordDos.size();
            // 计算子阵的平均发电量
            double submatrixAvgEff = validEfficiencyRecordDos.stream().mapToDouble(StringEfficiencyRecordDO::getEfficiency).average().orElse(0);
            efficiencyRecordDos.parallelStream().forEach(rd -> {
                rd.setSubmatrixAvgE(submatrixAvgEff);
                rd.setSubmatrixStringNum(efficiencyRecordDos.size());
                rd.setSubmatrixStringValidNum(submatrixStringValidNum);
                if (RegexUtils.isStandardCodeSubmatrixPrefix(submatrixCode)) {
                    rd.setZone(submatrixCode.substring(0, 5));
                }
                if (null != rd.getEfficiency() && null != rd.getSubmatrixAvgE() && rd.getSubmatrixAvgE() != 0D) {
                    rd.setLossE((1D - rd.getEfficiency() / rd.getSubmatrixAvgE()) * 100D);
                }
                rd.setCreateTime(dateTime);
            });
        }
    }

    /**
     * 过滤掉组串 零电流支路判定，取早上11:00-11:30分钟级电流数据，判定30组数据，如果存在20组数据以上相同或小于1A，则判定该支路为零电流支路，下面过程不参与计算
     *
     * @param submatrixForwardStringDataDos
     * @param dataTimeGe
     * @param dataTimeLe
     */
    public static Collection<String> filterInvalidStringCurrent(List<ForwardStringDataDO> submatrixForwardStringDataDos, LocalDateTime dataTimeGe, LocalDateTime dataTimeLe) {
        if (CollectionUtils.isNotEmpty(submatrixForwardStringDataDos)) {
            // 按组串分组
            Map<String, List<ForwardStringDataDO>> forwardStringDataDosMap = submatrixForwardStringDataDos.stream().collect(Collectors.groupingBy(ForwardStringDataDO::getDeviceCode));
            Set<String> excludeStringCodeSet = new HashSet<>(forwardStringDataDosMap.size());
            for (Map.Entry<String, List<ForwardStringDataDO>> entry : forwardStringDataDosMap.entrySet()) {
                List<ForwardStringDataDO> subForwardStringDataDos = entry.getValue().stream().filter(fsd -> fsd.getDataTime().compareTo(dataTimeGe) >= 0 && fsd.getDataTime().compareTo(dataTimeLe) <= 0).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(subForwardStringDataDos)) {
                    int zeroCount = 0;
                    HashMap<Double, Integer> countMap = new HashMap<>();
                    int nullVoltageCount = 0;
//                    int nullCurrentCount = 0;
                    for (ForwardStringDataDO stringDataDo : subForwardStringDataDos) {
                        // 电流连续值
                        Integer cnt = countMap.get(stringDataDo.getElectricCurrent());
                        if (null == cnt) {
                            cnt = 0;
                        }
                        cnt++;
                        countMap.put(stringDataDo.getElectricCurrent(), cnt);
                        // 电流小于1A
                        if (null == stringDataDo.getElectricCurrent() || stringDataDo.getElectricCurrent() < 1D) {
                            if (null == stringDataDo.getElectricCurrent()) {
                                log.error("过滤设备电压电流数据-组串电流为空: {}", stringDataDo);
                            }
                            zeroCount++;
                        }
                        if (null == stringDataDo.getVoltage()) {
                            nullVoltageCount++;
                        }
                    }
                    for (Integer cnt : countMap.values()) {
                        if (null != cnt && cnt >= 20) {
                            Iterator<ForwardStringDataDO> iterator = subForwardStringDataDos.iterator();
                            while (iterator.hasNext()) {
                                ForwardStringDataDO stringDataDo = iterator.next();
                                if (Objects.equals(entry.getKey(), stringDataDo.getDeviceCode())) {
                                    iterator.next();
                                    if (!excludeStringCodeSet.contains(entry.getKey())) {
                                        excludeStringCodeSet.add(entry.getKey());
                                    }
                                }
                            }


                        }
                    }
                    if (zeroCount >= 20 || nullVoltageCount >= 20) {
                        Iterator<ForwardStringDataDO> iterator = subForwardStringDataDos.iterator();
                        while (iterator.hasNext()) {
                            ForwardStringDataDO stringDataDo = iterator.next();
                            if (Objects.equals(entry.getKey(), stringDataDo.getDeviceCode())) {
                                iterator.next();
                                if (excludeStringCodeSet.contains(entry.getKey())) {
                                    excludeStringCodeSet.add(entry.getKey());
                                }
                            }
                        }

                    }
                }
            }
            return excludeStringCodeSet;
        }
        return Collections.EMPTY_LIST;
    }

    public static void filterInValid(Collection<String> allValidDeviceCodeIn, List<ForwardStringDataDO> submatrixForwardStringDataDos) {
        if (CollectionUtils.isNotEmpty(allValidDeviceCodeIn) && CollectionUtils.isNotEmpty(submatrixForwardStringDataDos)) {
            Iterator<ForwardStringDataDO> iterator = submatrixForwardStringDataDos.iterator();
            while (iterator.hasNext()) {
                ForwardStringDataDO stringDataDo = iterator.next();
                if (!allValidDeviceCodeIn.contains(stringDataDo.getDeviceCode())) {
                    iterator.remove();
                }
            }
        }
    }

    public static List<StringEfficiencyRecordDO> transferStringEfficiencyRecord(List<StringEfficiencyDTO> efficiencyDtos, LocalDateTime dataTime, String zoneCode, String submatrixCode) {
        if (CollectionUtils.isNotEmpty(efficiencyDtos)) {
            List<StringEfficiencyRecordDO> result = efficiencyDtos.stream().map(d -> {
                StringEfficiencyRecordDO recordDo = new StringEfficiencyRecordDO(d.getTenantNo(), d.getOrganNo(), d.getDevicePid(),
                        d.getDeviceId(), d.getDeviceName(), d.getDeviceCode(), dataTime, d.getEfficiency(), null,
                        null, null, submatrixCode, zoneCode, null);
                recordDo.setExinvalid(d.getExinvalid());
                return recordDo;
            }).collect(Collectors.toList());
            return result;
        }
        return null;
    }

    public static List<StringEfficiencyDTO> calculateStringEfficiency(List<ForwardStringDataDO> dataDos) {
        log.info("计算组串能效/发电量开始...");
        if (null != dataDos && dataDos.size() > 0) {
            List<StringDataPowerDTO> stringPowerDtos = calculateStringPower(dataDos);
            Map<String, List<StringDataPowerDTO>> stringPowerDataMap = stringPowerDtos.stream().collect(Collectors.groupingBy(StringDataPowerDTO::getDeviceCode));
            if (null != stringPowerDataMap && stringPowerDataMap.size() > 0) {
                List<StringEfficiencyDTO> efficiencyDtos = new ArrayList<>(stringPowerDataMap.size());
                for (Map.Entry<String, List<StringDataPowerDTO>> entry : stringPowerDataMap.entrySet()) {
                    List<StringDataPowerDTO> powerDtos = entry.getValue();
                    if (CollectionUtils.isNotEmpty(powerDtos)) {
                        log.info("[{}-{}]按时间排序", entry.getKey(), powerDtos.size());
                        powerDtos.stream().sorted(new Comparator<StringDataPowerDTO>() {
                            @Override
                            public int compare(StringDataPowerDTO o1, StringDataPowerDTO o2) {
                                return o1.getDataTime().compareTo(o2.getDataTime());
                            }
                        });
                        StringEfficiencyDTO efficiencyDto = new StringEfficiencyDTO(powerDtos.get(0).getTenantNo(),
                                powerDtos.get(0).getOrganNo(),
                                powerDtos.get(0).getDevicePid(),
                                powerDtos.get(0).getDeviceId(),
                                powerDtos.get(0).getDeviceCode(),
                                powerDtos.get(0).getDeviceName(),
                                0D,
                                Constant.BYTE_0
                        );
                        for (int i = 0; i < powerDtos.size() - 1; i++) {
                            StringDataPowerDTO dto1 = powerDtos.get(i);
                            StringDataPowerDTO dto2 = powerDtos.get(i + 1);
                            long diffSeconds = dto1.getDataTime().until(dto2.getDataTime(), ChronoUnit.SECONDS);
                            if (null != dto1.getP() && null != dto2.getP()) {
                                double s = (dto1.getP() + dto2.getP()) / 2 * diffSeconds;
//                                double elec = CalculateUtils.calElec(s);
//                                log.info("[{}: {}~{}]: {}, {}", dto1.getDeviceCode(), dto1.getDataTime(), dto2.getDataTime(), s, elec);
                                efficiencyDto.setEfficiency(efficiencyDto.getEfficiency() + CalculateUtils.calElec(s));
                            }

                        }
                        efficiencyDtos.add(efficiencyDto);
                    }
                }
                log.info("计算组串能效/发电量结束...");
                return efficiencyDtos;
            }
        }
        return null;
    }

    private static List<StringDataPowerDTO> calculateStringPower(List<ForwardStringDataDO> dataDos) {
        log.info("计算组串各时刻功率");
        if (CollectionUtils.isNotEmpty(dataDos)) {
            List<StringDataPowerDTO> result = dataDos.parallelStream().map(d -> {
                StringDataPowerDTO dto = new StringDataPowerDTO(d.getTenantNo(), d.getOrganNo(), d.getDevicePid(), d.getDeviceId(), d.getDeviceName(), d.getDeviceCode(), d.getDataTime());
                if (null != d.getVoltage() && null != d.getElectricCurrent() && d.getVoltage() >= 0D && d.getVoltage() <= 1500D && d.getElectricCurrent() >= 0D && d.getElectricCurrent() <= 25D) {
//                    dto.setP(CalculateUtils.multiply(null != d.getVoltage() ? d.getVoltage() : 0, null != d.getElectricCurrent() ? d.getElectricCurrent() : 0, 2));
                    dto.setP(CalculateUtils.multiplyDouble(d.getVoltage(), d.getElectricCurrent()));
                }
                return dto;
            }).collect(Collectors.toList());
            return result;
        }
        return null;
    }

    public static List<ForwardStringDataDO> processInverterData(List<ForwardDeviceInfoDO> forwardDeviceInfoDos, List<Map<String, Object>> resultDataMapList, LocalDateTime dateTime) {
        if (null != resultDataMapList && resultDataMapList.size() > 0 && null != forwardDeviceInfoDos && forwardDeviceInfoDos.size() > 0) {
            int stringCntPerInv = 18;
//            List<ForwardInverterDataDO> dataDos = new ArrayList<>(forwardDeviceInfoDos.size());
            List<ForwardStringDataDO> stringDataDos = new ArrayList<>(resultDataMapList.size());
            Map<String, Object> distinctMap = new HashMap<>(forwardDeviceInfoDos.size() * stringCntPerInv);
            for (ForwardDeviceInfoDO forwardDeviceInfoDo : forwardDeviceInfoDos) {
                ForwardInverterDataDO dataDo = new ForwardInverterDataDO();
                dataDo.setTenantNo(forwardDeviceInfoDo.getTenantNo());
                dataDo.setOrganNo(forwardDeviceInfoDo.getOrganNo());
                dataDo.setDeviceId(forwardDeviceInfoDo.getDeviceId());
                dataDo.setDeviceName(forwardDeviceInfoDo.getDeviceName());
                dataDo.setCreateTime(dateTime);
                for (Map<String, Object> dataMap : resultDataMapList) {
                    Object deviceIdObj = dataMap.get("device_id");
                    if (null != deviceIdObj) {
                        String deviceId = deviceIdObj.toString();
                        if (deviceId.equals(forwardDeviceInfoDo.getDeviceUid())) {
                            Object tmstampObj = dataMap.get("tmstamp");
                            LocalDateTime dataTime = null;
                            if (null != tmstampObj) {
                                String tmstampStr = tmstampObj.toString();
                                if (tmstampStr.contains(".0")) {
                                    tmstampStr = tmstampStr.replaceAll("\\.0", "");
                                }
                                dataTime = LocalDateTimeUtils.str2Time(tmstampStr);
                                dataDo.setDataTime(dataTime);
                            }
                            // 功率
                            Object totalOutputObj = dataMap.get("fd_pvi_pac");
                            if (null != totalOutputObj) {
                                dataDo.setTotalOutput(Double.parseDouble(totalOutputObj.toString()));
                            }
                            // 直流输入功率
                            Object pvInputPowerObj = dataMap.get("fd_pvi_pin");
                            if (null != pvInputPowerObj) {
                                dataDo.setPvInputPower(Double.parseDouble(pvInputPowerObj.toString()));
                            }
                            // 当日发电量
                            Object dayPowerGenerationObj = dataMap.get("fd_pvi_ed");
                            if (null != dayPowerGenerationObj) {
                                dataDo.setPowerGeneration(Double.parseDouble(dayPowerGenerationObj.toString()));
                            }
                            // 月发电量
                            Object monthPowerGenerationObj = dataMap.get("fd_pvi_em");
                            if (null != monthPowerGenerationObj) {
//                                dataDo.setPowerGeneration(Double.parseDouble(monthPowerGenerationObj.toString()));
                            }
                            // 年发电量
                            Object yearPowerGenerationObj = dataMap.get("fd_pvi_ey");
                            if (null != yearPowerGenerationObj) {
//                                dataDo.setPowerGeneration(Double.parseDouble(monthPowerGenerationObj.toString()));
                            }
                            // 累计发电量
                            Object totalPowerGenerationObj = dataMap.get("fd_pvi_ettl");
                            if (null != totalPowerGenerationObj) {
//                                dataDo.setPowerGeneration(Double.parseDouble(monthPowerGenerationObj.toString()));
                            }
                            processInverterStringData(stringDataDos, forwardDeviceInfoDo.getTenantNo(), forwardDeviceInfoDo.getOrganNo(), forwardDeviceInfoDo.getDeviceId(), forwardDeviceInfoDo.getDeviceName(), forwardDeviceInfoDo.getDeviceCode(), dataMap, stringCntPerInv, dataTime, dateTime, distinctMap);
//                            dataDos.add(dataDo);
                        }
                    }
                }
            }
            return stringDataDos;
        }
        return Collections.emptyList();
    }

    private static void processInverterStringData(List<ForwardStringDataDO> forwardStringDataDos, Integer tenantNo, Integer organNo, Long devicePid, String deviceName, String deviceCode, Map<String, Object> dataMap, int stringCnt, LocalDateTime dataTime, LocalDateTime createTime, Map<String, Object> distinctMap) {
        if (null != forwardStringDataDos && stringCnt > 0 && null != dataMap && dataMap.size() > 0) {

            for (int i = 1; i <= stringCnt; i++) {
                String pviKey = "fd_pvi_pv" + i + "i";
                String pvuKey = "fd_pvi_pv" + i + "u";
                String mapuk = tenantNo + "_" + organNo + "_" + devicePid + "_" + i + "_" + LocalDateTimeUtils.time2Str(dataTime, "yyyy-MM-dd HH:mm:ss");
                if (distinctMap.containsKey(mapuk)) {
                    continue;
                } else {
                    distinctMap.put(mapuk, null);
                }
                Object pviObj = dataMap.get(pviKey);
                Double voltage = null;
                Double electricCurrent = null;
                if (null != pviObj) {
                    electricCurrent = Double.parseDouble(pviObj.toString());
                }
                Object pvuObj = dataMap.get(pvuKey);
                if (null != pvuObj) {
                    voltage = Double.parseDouble(pvuObj.toString());
                }
                ForwardStringDataDO dataDo = new ForwardStringDataDO(tenantNo, organNo, devicePid, Long.valueOf(i), deviceName, deviceCode + "-" + String.format("%02d", i), voltage, electricCurrent, createTime, dataTime);
                forwardStringDataDos.add(dataDo);
            }
        }
    }

    public static List<ForwardStringDataDO> processCombinerboxData(List<ForwardDeviceInfoDO> forwardDeviceInfoDos, List<Map<String, Object>> resultDataMapList, LocalDateTime dateTime) {
        if (CollectionUtils.isNotEmpty(forwardDeviceInfoDos) && null != resultDataMapList && resultDataMapList.size() > 0) {
            int stringCntPerInv = 18;
//            List<ForwardCombinerBoxDataDO> dataDos = new ArrayList<>(forwardDeviceInfoDos.size());
            List<ForwardStringDataDO> stringDataDos = new ArrayList<>(forwardDeviceInfoDos.size() * stringCntPerInv);
            Map<String, Object> distinctMap = new HashMap<>(forwardDeviceInfoDos.size() * stringCntPerInv);
            for (ForwardDeviceInfoDO forwardDeviceInfoDo : forwardDeviceInfoDos) {
                ForwardCombinerBoxDataDO dataDo = new ForwardCombinerBoxDataDO();
                dataDo.setTenantNo(forwardDeviceInfoDo.getTenantNo());
                dataDo.setOrganNo(forwardDeviceInfoDo.getOrganNo());
                dataDo.setDeviceId(forwardDeviceInfoDo.getDeviceId());
                dataDo.setDeviceName(forwardDeviceInfoDo.getDeviceName());
                dataDo.setCreateTime(dateTime);
                for (Map<String, Object> dataMap : resultDataMapList) {
                    Object deviceIdObj = dataMap.get("device_id");
                    if (null != deviceIdObj) {
                        String deviceId = deviceIdObj.toString();
                        if (deviceId.equals(forwardDeviceInfoDo.getDeviceUid())) {
                            Object tmstampObj = dataMap.get("tmstamp");
                            LocalDateTime dataTime = null;
                            if (null != tmstampObj) {
                                String tmstampStr = tmstampObj.toString();
                                if (tmstampStr.contains(".0")) {
                                    tmstampStr = tmstampStr.replaceAll("\\.0", "");
                                }
                                dataTime = LocalDateTimeUtils.str2Time(tmstampStr);
                                dataDo.setDataTime(dataTime);
                            }
                            // 输出功率
                            Object outputPObj = dataMap.get("fd_cm_ycscgl");
                            if (null != outputPObj) {
                            }
                            // 输出电压
                            Object outputUObj = dataMap.get("fd_cm_ycscdy");
                            if (null != outputUObj) {
                            }
                            // 输出电流
                            Object outputIObj = dataMap.get("fd_cm_ycscdl");
                            if (null != outputIObj) {
                            }
                            processCombinerboxStringData(stringDataDos, forwardDeviceInfoDo.getTenantNo(), forwardDeviceInfoDo.getOrganNo(), forwardDeviceInfoDo.getDeviceId(), forwardDeviceInfoDo.getDeviceName(), forwardDeviceInfoDo.getDeviceCode(), dataMap, null != forwardDeviceInfoDo.getPvstrNum() ? forwardDeviceInfoDo.getPvstrNum() : stringCntPerInv, dataTime, dateTime, distinctMap);
//                            dataDos.add(dataDo);
                            break;
                        }
                    }
                }
            }
            return stringDataDos;
        }
        return Collections.emptyList();
    }

    private static void processCombinerboxStringData(List<ForwardStringDataDO> forwardStringDataDos, Integer tenantNo, Integer organNo, Long devicePid, String deviceName, String deviceCode, Map<String, Object> dataMap, int stringCnt, LocalDateTime dataTime, LocalDateTime createTime, Map<String, Object> distinctMap) {
        if (null != forwardStringDataDos && stringCnt > 0 && null != dataMap && dataMap.size() > 0) {
            // fd_cm_yc1zlsrdl
            // fd_cm_yc1zlsrdy
            // fd_cm_yc1zlsrgl
            for (int i = 1; i <= stringCnt; i++) {
                String pviKey = "fd_cm_yc" + i + "zlsrdl";
                String pvuKey = "fd_cm_yc" + i + "zlsrdy";
                String pvpKey = "fd_cm_yc" + i + "zlsrgl";
                String mapuk = tenantNo + "_" + organNo + "_" + devicePid + "_" + i + "_" + LocalDateTimeUtils.time2Str(dataTime, "yyyy-MM-dd HH:mm:ss");
                if (distinctMap.containsKey(mapuk)) {
                    continue;
                } else {
                    distinctMap.put(mapuk, null);
                }
                Object pviObj = dataMap.get(pviKey);
                Double voltage = null;
                Double electricCurrent = null;
                Double p = null;
                if (null != pviObj) {
                    electricCurrent = Double.parseDouble(pviObj.toString());
                }
                Object pvuObj = dataMap.get(pvuKey);
                if (null != pvuObj) {
                    voltage = Double.parseDouble(pvuObj.toString());
                }
                Object pvpObj = dataMap.get(pvpKey);
                if (null != pvpObj) {
                    p = Double.parseDouble(pvpObj.toString());
                }
                ForwardStringDataDO dataDo = new ForwardStringDataDO(tenantNo, organNo, devicePid, Long.valueOf(i), deviceName, deviceCode + "-" + String.format("%02d", i), voltage, electricCurrent, createTime, dataTime);
                forwardStringDataDos.add(dataDo);
            }
        }
    }

    public static void calculateStringIURecordBySubmatrix(Integer organNo, String submatrixCode, LocalDateTime dataTime, LocalDateTime dateTime,
                                                          List<ForwardStringDataDO> submatrixForwardStringDataDos,
                                                          List<ForwardStringLeDiagnoseRecordDO> stringLeDiagnoseRecordResults) {
        // 零电流支路判定，取早上11:00-11:30分钟级电流数据，判定30组数据，如果存在20组数据以上相同或小于1A，则判定该支路为零电流支路，下面过程不参与计算
        Collection<String> excludeStringCodeSet = ForwardDeviceDataHelper.filterInvalidStringCurrent(submatrixForwardStringDataDos,
                dataTime.withHour(11).withMinute(1).withSecond(0).withNano(0),
                dataTime.withHour(11).withMinute(30).withSecond(0).withNano(0));
        if (CollectionUtils.isEmpty(submatrixForwardStringDataDos)) {
            log.info("组串IU低效诊断分析-过滤设备电压电流数据后无数据: {}, {}", organNo, submatrixCode);
            return;
        }
        List<ForwardStringLeDiagnoseRecordDO> recordDos = calculateStringIURecord(submatrixForwardStringDataDos, submatrixCode, dataTime, dateTime, excludeStringCodeSet);
        if (CollectionUtils.isNotEmpty(recordDos)) {
            stringLeDiagnoseRecordResults.addAll(recordDos);
        }
    }

    public static List<ForwardStringLeDiagnoseRecordDO> calculateStringIURecord(List<ForwardStringDataDO> submatrixForwardStringDataDos, String submatrixCode, LocalDateTime dataTime, LocalDateTime dateTime, Collection<String> excludeStringCodeSet) {
        if (CollectionUtils.isNotEmpty(submatrixForwardStringDataDos)) {
            // 按照组串分组
            Map<String, List<ForwardStringDataDO>> forwardStringDataMap = submatrixForwardStringDataDos.stream().collect(Collectors.groupingBy(ForwardStringDataDO::getDeviceCode));
            List<ForwardStringLeDiagnoseRecordDO> leDiagnoseRecordDos = new ArrayList<>(forwardStringDataMap.size());
            for (Map.Entry<String, List<ForwardStringDataDO>> entry : forwardStringDataMap.entrySet()) {
//                log.info("组串IU低效诊断分析-电流电压中位数{}", entry.getKey());
                List<ForwardStringDataDO> forwardStringDataDos = entry.getValue();
                // 求电流电压中位数值
                Double medianI = null;
                Double medianU = null;
                int mod = forwardStringDataDos.size() % 2;
                int idx = forwardStringDataDos.size() / 2;

                // 电流中位数
                forwardStringDataDos = forwardStringDataDos.stream().sorted(Comparator.comparing(ForwardStringDataDO::getElectricCurrent, Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList());

                if (mod == 1) {
                    medianI = forwardStringDataDos.get(idx).getElectricCurrent();
                } else {
                    Double sumMedianI = CalculateUtils.plus(forwardStringDataDos.get(idx - 1).getElectricCurrent(), forwardStringDataDos.get(idx).getElectricCurrent());
                    medianI = null == sumMedianI ? null : sumMedianI / 2;
                }
                // 电压中位数
                forwardStringDataDos = forwardStringDataDos.stream().sorted(Comparator.comparing(ForwardStringDataDO::getVoltage, Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList());
                if (mod == 1) {
                    medianU = forwardStringDataDos.get(idx).getVoltage();
                } else {
                    Double sumMedianU = CalculateUtils.plus(forwardStringDataDos.get(idx - 1).getVoltage(), forwardStringDataDos.get(idx).getVoltage());
                    medianU = null == sumMedianU ? null : sumMedianU / 2;
                }
                ForwardStringLeDiagnoseRecordDO leDiagnoseRecordDo = new ForwardStringLeDiagnoseRecordDO(entry.getValue().get(0).getTenantNo(),
                        entry.getValue().get(0).getOrganNo(), entry.getValue().get(0).getDevicePid(), entry.getValue().get(0).getDeviceId(),
                        entry.getValue().get(0).getDeviceName(), entry.getValue().get(0).getDeviceCode(), submatrixCode);
                leDiagnoseRecordDo.setSubmatrix(submatrixCode);
                leDiagnoseRecordDo.setMedianI(medianI);
                leDiagnoseRecordDo.setMedianU(medianU);
                leDiagnoseRecordDo.setDiagnoseTime(dataTime);
                leDiagnoseRecordDo.setCreateTime(dateTime);
                leDiagnoseRecordDos.add(leDiagnoseRecordDo);
            }
            if (CollectionUtils.isNotEmpty(leDiagnoseRecordDos)) {

                List<ForwardStringLeDiagnoseRecordDO> validLeDiagnoseRecordDos = leDiagnoseRecordDos;
                if (CollectionUtils.isNotEmpty(excludeStringCodeSet)) {
                    validLeDiagnoseRecordDos = leDiagnoseRecordDos.stream().filter(rd -> !excludeStringCodeSet.contains(rd.getDeviceCode())).collect(Collectors.toList());
                }
                // 求子阵电流电压平均中位值
                // 子阵平均电流
                OptionalDouble averageI = validLeDiagnoseRecordDos.stream().filter(d -> null != d.getMedianI()).map(ForwardStringLeDiagnoseRecordDO::getMedianI).mapToDouble(Double::valueOf).average();
                // 子阵平均电压
                OptionalDouble averageU = validLeDiagnoseRecordDos.stream().filter(d -> null != d.getMedianU()).map(ForwardStringLeDiagnoseRecordDO::getMedianU).mapToDouble(Double::valueOf).average();
                validLeDiagnoseRecordDos.sort(new Comparator<ForwardStringLeDiagnoseRecordDO>() {
                    @Override
                    public int compare(ForwardStringLeDiagnoseRecordDO o1, ForwardStringLeDiagnoseRecordDO o2) {
                        return o1.getDeviceCode().compareTo(o2.getDeviceCode());
                    }
                });
                leDiagnoseRecordDos.stream().forEach(v -> {
                    if (averageU.isPresent()) {
                        v.setSubmatrixAvgMedianU(averageU.getAsDouble());
                    }
                    if (averageI.isPresent()) {
                        v.setSubmatrixAvgMedianI(averageI.getAsDouble());
                    }
                    v.setSubmatrixStringNum(leDiagnoseRecordDos.size());
                    // （１－（单组串电流平均值／子阵所有组串的平均电流））＊１００％＞２％，则判定该组串为低效电流。
                    // 组串的电流损失比例：组串的电流损失百分比例／该子阵的所有组串数量
                    // 此处计算阈值
                    if (null != v.getMedianI() && null != v.getSubmatrixAvgMedianI() && 0 != v.getSubmatrixAvgMedianI()) {
                        double rate = (1D - v.getMedianI() / v.getSubmatrixAvgMedianI()) * 100;
                        v.setLowI(rate);
                        v.setLossI(rate / v.getSubmatrixStringNum());
                    }
                    // （１－（单组串电压平均值／子阵所有组串的平均电压））＊１００％＞２％，则判定该组串为低效电压。
                    // 组串的电压损失比例：组串电压的损失百分比例／该子阵的所有组串数量
                    // 此处计算阈值
                    if (null != v.getMedianU() && null != v.getSubmatrixAvgMedianU() && 0 != v.getSubmatrixAvgMedianU()) {
                        double rate = (1D - v.getMedianU() / v.getSubmatrixAvgMedianU()) * 100;
                        v.setLowU(rate);
                        v.setLossU(rate / v.getSubmatrixStringNum());
                    }

                    //｛１－[(单组串电压平均值＊单组串电流平均值)／(子阵所有组串的平均电流＊子阵所有组串的平均电压)]｝＊１００％
                    // 组串的功率损失比例：组串的功率损失百分比例／该子阵的所有组串数量
                    // 此处计算阈值
                    if (null != v.getMedianI() && null != v.getMedianU() && null != v.getSubmatrixAvgMedianI() && null != v.getSubmatrixAvgMedianU() && 0 != v.getSubmatrixAvgMedianI() && 0 != v.getSubmatrixAvgMedianU()) {
//                        Double stringP = ((null != v.getAvgI() && null != v.getAvgU()) ? (v.getAvgI() * v.getAvgU()) : null);
                        Double stringP = ((null != v.getMedianI() && null != v.getMedianU()) ? (v.getMedianI() * v.getMedianU()) : null);
                        Double submatrixP = ((null != v.getSubmatrixAvgMedianI() && null != v.getSubmatrixAvgMedianU()) ? (v.getSubmatrixAvgMedianU() * v.getSubmatrixAvgMedianI()) : null);
                        double rate = (1D - stringP / submatrixP) * 100;
                        v.setLossP(rate / v.getSubmatrixStringNum());
                    }
                });
            }
            return leDiagnoseRecordDos;
        }
        return null;
    }


    public static void exclude(Collection<String> excludeStringCodeSet, List<ForwardStringLeDiagnoseRecordDO> submatrixStringLeDiagnoseRecordDos) {
        if (null != submatrixStringLeDiagnoseRecordDos && submatrixStringLeDiagnoseRecordDos.size() > 0 && null != excludeStringCodeSet && excludeStringCodeSet.size() > 0) {
            Iterator<ForwardStringLeDiagnoseRecordDO> iterator = submatrixStringLeDiagnoseRecordDos.iterator();
            while (iterator.hasNext()) {
                ForwardStringLeDiagnoseRecordDO diagnoseRecordDo = iterator.next();
                if (excludeStringCodeSet.contains(diagnoseRecordDo.getDeviceCode())) {
                    iterator.remove();
                }
            }
        }
    }

    /**
     * 过滤掉组串 零电流支路判定，取早上11:00-11:30分钟级电流数据，判定30组数据，如果存在20组数据以上相同或小于1A，则判定该支路为零电流支路，下面过程不参与计算
     *
     * @param submatrixForwardStringDataDos
     * @param dataTimeGe
     * @param dataTimeLe
     */
    public static StringVoltageCurrentInvalidDataDTO getInvalidStringVoltageCurrent(List<ForwardStringDataDO> submatrixForwardStringDataDos,
                                                                                    LocalDateTime dataTimeGe, LocalDateTime dataTimeLe) {
        StringVoltageCurrentInvalidDataDTO dto = new StringVoltageCurrentInvalidDataDTO();
        if (CollectionUtils.isNotEmpty(submatrixForwardStringDataDos)) {
            // 按组串分组
            Map<String, List<ForwardStringDataDO>> forwardStringDataDosMap = submatrixForwardStringDataDos.stream().collect(Collectors.groupingBy(ForwardStringDataDO::getDeviceCode));
            for (Map.Entry<String, List<ForwardStringDataDO>> entry : forwardStringDataDosMap.entrySet()) {
                // 单个组串的一组数据
                List<ForwardStringDataDO> subForwardStringDataDos = entry.getValue().stream().filter(fsd -> fsd.getDataTime().compareTo(dataTimeGe) >= 0 && fsd.getDataTime().compareTo(dataTimeLe) <= 0).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(subForwardStringDataDos)) {
                    // 电流为0或小于1A的次数
                    int zeroCurrentCount = 0;
                    // 电压为0或小于1V的次数
                    int zeroVoltageCount = 0;
                    // 电流为null的次数
                    int nullCurrentCount = 0;
                    // 电压为null的次数
                    int nullVoltageCount = 0;
                    // 电流连续相同值
                    HashMap<Double, Integer> continuousCurrentCountMap = new HashMap<>();
                    // 电压连续相同值
                    HashMap<Double, Integer> continuousVoltageCountMap = new HashMap<>();
                    for (ForwardStringDataDO stringDataDo : subForwardStringDataDos) {
                        // 电流连续值
                        Integer continuousCurrentCnt = continuousCurrentCountMap.get(stringDataDo.getElectricCurrent());
                        if (null == continuousCurrentCnt) {
                            continuousCurrentCnt = 0;
                        }
                        continuousCurrentCnt++;
                        continuousCurrentCountMap.put(stringDataDo.getElectricCurrent(), continuousCurrentCnt);

                        // 电压连续值
                        Integer continuousVoltageCnt = continuousVoltageCountMap.get(stringDataDo.getVoltage());
                        if (null == continuousVoltageCnt) {
                            continuousVoltageCnt = 0;
                        }
                        continuousVoltageCnt++;
                        continuousVoltageCountMap.put(stringDataDo.getVoltage(), continuousVoltageCnt);

                        // 电流null值
                        if (null == stringDataDo.getElectricCurrent()) {
                            nullCurrentCount++;
                        }
                        // 电压null值
                        if (null == stringDataDo.getVoltage()) {
                            nullVoltageCount++;
                        }
                        // 电流为0或小于1A
                        if (null != stringDataDo.getElectricCurrent() && stringDataDo.getElectricCurrent() < 1D) {
                            zeroCurrentCount++;
                        }
                        // 电压为0或小于1V
                        if (null != stringDataDo.getVoltage() && stringDataDo.getVoltage() < 1D) {
                            zeroVoltageCount++;
                        }
                    }

                    if (zeroCurrentCount >= 20) {
                        if (!dto.getZeroCurrentCodeIn().contains(entry.getKey())) {
                            dto.getZeroCurrentCodeIn().add(entry.getKey());
                        }
                    }
                    if (zeroVoltageCount >= 20) {
                        if (!dto.getZeroVoltageCodeIn().contains(entry.getKey())) {
                            dto.getZeroVoltageCodeIn().add(entry.getKey());
                        }
                    }
                    if (nullCurrentCount >= 20) {
                        if (!dto.getNullCurrentCodeIn().contains(entry.getKey())) {
                            dto.getNullCurrentCodeIn().add(entry.getKey());
                        }
                    }
                    if (nullVoltageCount >= 20) {
                        if (!dto.getNullVoltageCodeIn().contains(entry.getKey())) {
                            dto.getNullVoltageCodeIn().add(entry.getKey());
                        }
                    }

                    for (Integer continuousCnt : continuousCurrentCountMap.values()) {
                        if (null != continuousCnt && continuousCnt >= 20) {
                            if (!dto.getContinuousCurrentCodeIn().contains(entry.getKey())) {
                                dto.getContinuousCurrentCodeIn().add(entry.getKey());
                            }
                            break;
                        }
                    }
                    for (Integer continuousCnt : continuousVoltageCountMap.values()) {
                        if (null != continuousCnt && continuousCnt >= 20) {
                            if (!dto.getContinuousVoltageCodeIn().contains(entry.getKey())) {
                                dto.getContinuousVoltageCodeIn().add(entry.getKey());
                            }
                            break;
                        }
                    }
                }
            }
        }
        dto.mergeExcludeStringCodeIn();
        return dto;
    }

    public static void markStringEfficiencyRecordInvalidVoltageCurrent(StringVoltageCurrentInvalidDataDTO invalidDataDto, List<StringEfficiencyDTO> efficiencyDtos) {
        if (null != invalidDataDto && CollectionUtils.isNotEmpty(efficiencyDtos)) {
            for (StringEfficiencyDTO efficiencyDto : efficiencyDtos) {
                // 0电流
                if (invalidDataDto.getZeroCurrentCodeIn().contains(efficiencyDto.getDeviceCode())) {
                    efficiencyDto.setExinvalid(Constant.BYTE_0);
                    continue;
                }
                // 0电压
                if (invalidDataDto.getZeroVoltageCodeIn().contains(efficiencyDto.getDeviceCode())) {
                    efficiencyDto.setExinvalid(Constant.BYTE_2);
                    continue;
                }
                // null电流
                if (invalidDataDto.getNullCurrentCodeIn().contains(efficiencyDto.getDeviceCode())) {
                    efficiencyDto.setExinvalid(Constant.BYTE_3);
                    continue;
                }
                // null电压
                if (invalidDataDto.getNullVoltageCodeIn().contains(efficiencyDto.getDeviceCode())) {
                    efficiencyDto.setExinvalid(Constant.BYTE_4);
                    continue;
                }
                // 持续电流值
                if (invalidDataDto.getContinuousCurrentCodeIn().contains(efficiencyDto.getDeviceCode())) {
                    efficiencyDto.setExinvalid(Constant.BYTE_5);
                    continue;
                }
                // 持续电流值
                if (invalidDataDto.getContinuousVoltageCodeIn().contains(efficiencyDto.getDeviceCode())) {
                    efficiencyDto.setExinvalid(Constant.BYTE_6);
                    continue;
                }
                // 其他
                if(invalidDataDto.getExcludeStringCodeIn().contains(efficiencyDto.getDeviceCode())){
                    efficiencyDto.setExinvalid(Byte.parseByte("99"));
                }
            }
        }
    }

}
